package org.example.docsearcher.config;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;
import org.example.docsearcher.model.DocInfo;
import org.example.docsearcher.model.Relate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class Index {
    public static final String SAVE_PATH = "/root/docsearcher/";
    //前排索引
    public static List<DocInfo> forwardIndex = new ArrayList<>();
    //后排索引
    public static Map<String, List<Relate>> invertedIndex = new HashMap<>();

    public Object locker1 = new Object();
    public Object locker2 = new Object();

    //1. 通过docId,在正排索引中查询文档信息
    public DocInfo getForward(int docId) {
        return forwardIndex.get(docId);
    }

    //2. 通过一个关键词在倒排索引中查看相关文档
    public List<Relate> getInverted(String key) {
        return invertedIndex.get(key);
    }
    //3. 在索引中新增一个文档
    public void addDoc(String title, String url, String content) {
        //增加前排索引
        DocInfo docInfo = addForward(title, url, content);
        //增加后排索引
        addInverted(docInfo);
    }

    private void addInverted(DocInfo docInfo) {
        //通过分词统计每个词在文章中出现的次数来作为相关性权重
        Map<String, Integer> countMap = new HashMap<>();

        //统计标题中的词 权重为10
        String title = docInfo.getTitle();
        List<Term> terms = ToAnalysis.parse(title).getTerms();

        for(Term term : terms) {
            String key = term.getName();
            int count = countMap.getOrDefault(key, 0) + 10;
            countMap.put(key, count);
        }

        //统计正文中的词 权重为1
        String content = docInfo.getContent();
        terms = ToAnalysis.parse(content).getTerms();
        for(Term term : terms) {
            String key = term.getName();
            int count = countMap.getOrDefault(key, 0) + 1;
            countMap.put(key, count);
        }

        //添加到invertedIndex
        for(Map.Entry<String, Integer> entry : countMap.entrySet()) {
            String key = entry.getKey();
            int weight = entry.getValue();
            synchronized (locker2) {
                List<Relate> relates = invertedIndex.get(key);
                Relate relate = new Relate(key, docInfo.getDocId(), weight);
                if(relates == null) {
                    relates = new ArrayList<>();
                    relates.add(relate);
                    invertedIndex.put(key, relates);
                }else {
                    relates.add(relate);
                }
            }
        }
    }

    private DocInfo addForward(String title, String url, String content) {
        DocInfo docInfo = new DocInfo(title, url, content);
        synchronized (locker1) {
            docInfo.setDocId(forwardIndex.size());
            forwardIndex.add(docInfo);
        }
        return docInfo;
    }

    //4. 把索引保存到磁盘
    public void save() {
        System.out.println("保存索引开始");
        ObjectMapper mapper = new ObjectMapper();

        File file = new File(SAVE_PATH);
        //判断目录是否存在，不存在则创建目录
        if(!file.exists()) {
            file.mkdirs();
        }

        //使用两个文件分别保存正排索引和倒排索引
        File forward = new File(SAVE_PATH + "forward.txt");
        File inverted = new File(SAVE_PATH + "inverted.txt");
        try {
            mapper.writeValue(forward, forwardIndex);
            mapper.writeValue(inverted, invertedIndex);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        System.out.println("保存索引完成");
    }
    //5. 从磁盘加载索引
    public void load() {
        System.out.println("开始加载索引");
        ObjectMapper mapper = new ObjectMapper();
        File forward = new File(SAVE_PATH + "forward.txt");
        File inverted = new File(SAVE_PATH + "inverted.txt");
        try {
            forwardIndex = mapper.readValue(forward, new TypeReference<List<DocInfo>>() {});
            invertedIndex = mapper.readValue(inverted, new TypeReference<Map<String, List<Relate>>>() {});
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        System.out.println("加载索引完成");
    }
}
